package com.tuo51.foudation.utils;

import com.tuo51.foudation.exception.BusinessException;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.tuo51.foudation.model.ErrorCode.MAP_CONVERT_ERROR;

/**
 * ***********************
 * description :对象与map转化
 * Created by Tim
 * from com. matt
 * on 2018/6/22 0022.
 * ***********************
 */
public class MapConvertUtil {
    /**
     * 把对象转化为map集合形式
     * @param obj 需要转化的对象
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String,Object> obejctToMap(Object obj){
        try {
            Map<String, Object> map = new HashMap<>();
            Class<?> clazz = obj.getClass();
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                String fieldName = field.getName();
                Object value = field.get(obj);
                map.put(fieldName, value);
            }
            return map;
        }catch (Exception e) {
            throw new BusinessException(MAP_CONVERT_ERROR, "对象转map异常");
        }
    }

    /**
     * 把对象转化为map集合形式
     * @param map 需要转化的对象
     * @param clazz 需要转化的对象
     * @return
     */
    public static <T> T mapToObject(Map<String, Object> map, Class<T> clazz) {
        try {
            if (map == null)
                return null;

            T obj = clazz.newInstance();

            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                Method setter = property.getWriteMethod();

                if (setter != null) {
                    Object value = map.get(property.getName());
                    if (Objects.isNull(value)) {
                        continue;
                    }
                    Class innerClass = property.getPropertyType();
                    if (isNormalClass(innerClass)){
                        setter.invoke(obj, value);
                    } else if (List.class.isAssignableFrom(innerClass)){
                        fillInList((List)value);
                        setter.invoke(obj, value);
                    } else if (innerClass.isArray()){
                        throw new BusinessException("数组暂时不能转换");
//                        fillInList((List)value);
//                        setter.invoke(obj, value);
                    } else if (Map.class.isAssignableFrom(innerClass)){
                        Object o =  mapToObject((Map) value, innerClass);
                        setter.invoke(obj, o);
                    }

                }
            }
            return obj;
        } catch (BusinessException e){
            throw new BusinessException(e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            throw new BusinessException(MAP_CONVERT_ERROR, "map转对象异常");
        }
    }

//    private static <T> T[] toArray(List<T> list){
//        for (T t)
//        ArrayList
//
//        if (list.size() < size)
//            // Make a new array of a's runtime type, but my contents:
//            return (T[]) Arrays.copyOf(list, size, a.getClass());
//        System.arraycopy(elementData, 0, a, 0, size);
//        if (a.length > size)
//            a[size] = null;
//        return list.toArray();
//    }


    private static <T> void fillInList(List<T> list) {
        for (T m :list){
            mapToObject((Map<String, Object>) m, m.getClass());
        }
    }


    private static boolean isNormalClass(Class clazz){
         return clazz.equals(Integer.class) ||
                 clazz.equals(Byte.class) ||
                 clazz.equals(Long.class) ||
                 clazz.equals(Double.class) ||
                 clazz.equals(Float.class) ||
                 clazz.equals(Character.class) ||
                 clazz.equals(Short.class) ||
                 clazz.equals(Boolean.class) ||
                 clazz.equals(String.class);
    }
}
